import axios from 'axios'
import Decimal from 'decimal.js'
import { Server } from 'http'
import { random } from 'lodash'
import moment from 'moment'
import { Server as SocketServer } from 'socket.io'
import { redis, redisUtils } from '../redis'
import { BlindBoxGift, Dealer, Member, MemberPrizeRecord } from './db'

interface PoolLevelData {
    balance: string
    gifts: { price: string; title: string; enable: boolean }[]
}

interface Pool {
    id: number
    price: number
    locked: number[]
    level_2: PoolLevelData
    level_3: PoolLevelData
    level_4: PoolLevelData
}

const refreshState = async () => {
    //查询奖池
    const dealers = await Dealer.findAll({
        where: {
            enable: 1,
            is_del: 0,
        },
        order: [['price', 'DESC']],
    })

    //根据奖池查询奖品
    const gifts = await BlindBoxGift.findAll({
        where: {
            dealer_id: dealers.map((t) => t.id),
            is_del: 0,
        },
        order: [
            ['level', 'DESC'],
            ['price', 'DESC'],
        ],
    })

    const getPoolLevelData = (dealer: Dealer, level: 2 | 3 | 4): PoolLevelData => {
        const balance =
            level === 2 ? dealer.b_balance_pool : level === 3 ? dealer.c_balance_pool : dealer.d_balance_pool

        const available_gifts: { price: string; title: string; enable: boolean }[] = []
        const filted_gifts = gifts.filter((t) => t.dealer_id === dealer.id && t.level === level)

        for (const gift of filted_gifts) {
            const enable = balance.gte(gift.price)
            available_gifts.push({
                price: gift.price.toString(),
                title: gift.title,
                enable,
            })
            if (enable) break
        }

        return {
            balance: balance.toFixed(3),
            gifts: available_gifts.reverse(),
        }
    }

    //奖池分组
    const pools = dealers.map<Pool>((dealer) => {
        //盲盒的票数
        const price = Math.ceil(dealer.price.toNumber() / 8)

        return {
            id: dealer.id,
            price,
            locked: [],
            level_2: getPoolLevelData(dealer, 2),
            level_3: getPoolLevelData(dealer, 3),
            level_4: getPoolLevelData(dealer, 4),
        }
    })

    //读取锁定的池子
    for (const pool of pools) {
        const locked = await redisUtils.get<string>(`blind_box_lock:${pool.id}`)
        if (locked) {
            pool.locked = locked.split(',').map(Number)
        }
    }

    //特殊用户
    const special_users = await redisUtils.get<string>('blind_box_prize')
    const special_uids = special_users ? special_users.split(',') : []

    //不显示记录
    const no_record = (await redisUtils.get<string[]>('blind_box_no_record')) ? 1 : 0

    return {
        pools,
        special_uids,
        no_record,
    }
}

/**
 * 填充任务的数据
 */
interface FillTaskData {
    uid: string
    num: number
    dealer_id: number
}

export default function createBlindBoxServer(socketServer: SocketServer, server: Server) {
    socketServer.on('connection', (socket) => {
        socket.on('blind_box:get_state', async (ack?: () => void) => {
            socket.emit('blind_box:state', await refreshState())
            if (typeof ack === 'function') {
                ack()
            }
        })

        socket.on('blind_box:set_special_uids', async (uids: string[], ack?: () => void) => {
            if (uids.length === 0) {
                await redis.del('blind_box_prize')
            } else {
                await redisUtils.set('blind_box_prize', uids.join(','))
            }
            socket.emit('blind_box:state', await refreshState())
            if (typeof ack === 'function') {
                ack()
            }
        })

        socket.on('blind_box:set_flag', async (key: string, value: any, ack?: () => void) => {
            await redisUtils.set(key, value)
            socket.emit('blind_box:state', await refreshState())
            if (typeof ack === 'function') {
                ack()
            }
        })

        socket.on('blind_box:lock', async (id: number, locked: number[], ack?: () => void) => {
            if (locked.length === 0) {
                await redisUtils.del(`blind_box_lock:${id}`)
            } else {
                await redisUtils.set(`blind_box_lock:${id}`, locked.join(','))
            }
            socket.emit('blind_box:state', await refreshState())
            if (typeof ack === 'function') {
                ack()
            }
        })

        socket.on('blind_box:fill', async (task: FillTaskData) => {
            //填充抽奖数据
            const { uid, num, dealer_id } = task
            const interval = 8000

            //查询用户数据和盲盒礼物
            const member = await Member.findOne({ where: { uid } })
            if (!member) {
                return
            }

            //根据奖池查询奖品
            const gifts = await BlindBoxGift.findAll({
                where: {
                    dealer_id,
                    is_del: 0,
                    level: 1,
                },
            })
            if (gifts.length === 0) {
                return
            }

            let start = 0
            gifts.forEach((gift) => {
                gift.probability_num = new Decimal(gift.probability).mul(10000).floor().toNumber()
                gift.range_start = start
                gift.range_end = start + gift.probability_num
                start += gift.probability_num
            })

            /**
             * 返回一个随机礼物
             */
            const randomGift = () => {
                const max = gifts[gifts.length - 1].range_end
                const hit = random(max, true)
                return gifts.find((t) => hit >= t.range_start && hit < t.range_end)!
            }

            //开始填充数据
            for (let i = 1; i <= num; i++) {
                //随机选择一个礼物
                const prize = randomGift()

                //插入记录
                await MemberPrizeRecord.create({
                    create_time: moment().utcOffset('+08:00').format('YYYY/MM/DD HH:mm:ss'),
                    member_id: member.id,
                    name: prize.title,
                    nickname: member.nickname,
                    image: prize.image,
                    number: 1,
                    dealer_id,
                    head_image: member.head_image,
                    probability: prize.probability,
                    price: prize.price.toFixed(2),
                    level: prize.level,
                    currency_id: prize.currency_id,
                })

                //发送飘屏
                await axios.post('http://openapi.zhubei.cn/external/externalgame/floatingScreenV2', {
                    screen_type: 'mh',
                    msg_body: [
                        { color: '#FFFFFF', text: '恭喜' },
                        { color: '#FFF387', text: member.nickname },
                        { color: '#FFFFFF', text: '获得礼物：' },
                        { color: '#FFF387', text: prize.title },
                    ],
                })

                //通报进度事件
                socket.emit(`blind_box:fill_progress`, i, num)

                if (i < num) {
                    //如果还有下一个就等待
                    const timeout = Math.round(random(0.8, 1.2, true) * interval)
                    await new Promise<void>((resolve) => setTimeout(resolve, timeout))
                }
            }
        })
    })
}
